/**
 * Copyright ${license.git.copyrightYears} the original author or authors.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.ibatis.scripting.xmltags;

import org.apache.ibatis.builder.SqlSourceBuilder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.session.Configuration;

/**
 * 动态sql
 * DynamicSqlSource负责解析动态SQL语句，也是最常用的SqlSource实现之一
 * 解析时机是在实际执行SQL语句之前
 *
 * @author Clinton Begin
 */
public class DynamicSqlSource implements SqlSource {

    private final Configuration configuration;
    // SqlNode中使用了组合模式，形成了一个树状结构，DynamicSqlSource中使用rootSqlNode字段（SqlNode类型）记录了待解析的SqlNode树的根节点
    private final SqlNode rootSqlNode;

    public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
        this.configuration = configuration;
        this.rootSqlNode = rootSqlNode;
    }

    @Override
    public BoundSql getBoundSql(Object parameterObject) {
        // 创建DynamicContext对象
        DynamicContext context = new DynamicContext(configuration, parameterObject);
        // 会调用整个树的全部SqlNode.apply()
        rootSqlNode.apply(context);
        // 解析参数属性，将SQL语句中的“#{}”占位符替换成“？”占位符
        SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
        Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
        SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        // 将DynamicContext.bindings复制到additionalParameters
        context.getBindings().forEach(boundSql::setAdditionalParameter);
        return boundSql;
    }

}
